package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.product.api.domain.*;
import com.spzx.product.api.domain.vo.SkuPriceVo;
import com.spzx.product.api.domain.vo.SkuStockVo;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.ProductDetailsMapper;
import com.spzx.product.mapper.ProductMapper;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.IProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品 服务实现类
 * </p>
 *
 * @author dongdongxiaoyuanmao
 * @since 2025-03-06
 */
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private ProductDetailsMapper productDetailsMapper;

    @Autowired
    private SkuStockMapper skuStockMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisTemplate redisTemplate;

    //因为涉及到好几张表的增加操作，所以我们需要对该方法进行事务控制
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertProduct(Product product) {
        //保存商品SPU
        baseMapper.insert(product);

        //获取商品SKU集合
        List<ProductSku> productSkuList = product.getProductSkuList();
        for (int i = 0; i < product.getProductSkuList().size(); i++) {
            //保存商品SKU
            //通过索引获取sku元素
            ProductSku productSku = productSkuList.get(i);
            productSku.setProductId(product.getId());
            //设置skuCode，形式是商品的ID_索引
            productSku.setSkuCode(product.getId() + "_" + i);
            //设置skuName，形式是商品名称_商品规格
            productSku.setSkuName(product.getName() + "_" + productSku.getSkuSpec());
            //最后将数据录入到数据库中
            productSkuMapper.insert(productSku);

            //保存商品库存
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(productSku.getId());
            //商品刚刚创建出来，商品的总数量也就是商品的库存
            skuStock.setTotalNum(productSku.getStockNum());
            //商品刚刚创建出来，商品的可用库存数量也就是商品总数
            skuStock.setAvailableNum(productSku.getStockNum());
            //商品刚刚创建出来，还没有购买待付款中，所以商品的锁定库存数量为0
            skuStock.setLockNum(0);
            //商品刚刚创建出来，所以商品的销售数量为0
            skuStock.setSaleNum(0);
            //最后将数据录入到数据库中
            skuStockMapper.insert(skuStock);
        }

        //保存商品详情
        ProductDetails productDetails = new ProductDetails();
        productDetails.setProductId(product.getId());
        List<String> detailsImageUrlList = product.getDetailsImageUrlList();
        //多个图片的地址是用,分割开的
        productDetails.setImageUrls(String.join(",", detailsImageUrlList));
        //最后将数据录入到数据库中
        productDetailsMapper.insert(productDetails);

        return 1;
    }

    @Override
    public List<Product> selectProductList(Product product) {
        return baseMapper.selectProductList(product);
    }

    @Override
    public Product selectProductById(Long id) {
        //查询商品sku
        Product product = baseMapper.selectById(id);
        //查询商品spu
        LambdaQueryWrapper<ProductSku> productSkuLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //通过查询product_id来查询product_sku表，然后组装数据
        productSkuLambdaQueryWrapper.eq(ProductSku::getProductId, id);
        List<ProductSku> productSkuList = productSkuMapper.selectList(productSkuLambdaQueryWrapper);
        //将数据组装进product中
        product.setProductSkuList(productSkuList);

        //获取SKUIdList
        /*
        从一个包含ProductSku对象的列表中提取所有对象的id字段，并将这些id收集到一个新的List<Long>集合中
        stream()方法：将productSkuList转换成流
        map()方法：将productSkuList中的每个ProductSku对象映射到其id字段
        toList()方法：将映射后的结果转换为一个List集合
        代码传统等价写法：
        List<Long> skuIdList = new ArrayList<>();
        for (ProductSku productSku : productSkuList) {
            skuIdList.add(sku.getId());
        }
         */
        List<Long> skuIdList = productSkuList.stream().map(ProductSku::getId).toList();
        //查询库存
        LambdaQueryWrapper<SkuStock> skuStockLambdaQueryWrapper = new LambdaQueryWrapper<>();
        /*
        in()方法的作用是：查询sku_stock表中，sku_id字段值在skuIdList列表中的所有记录
        它对应的sql代码是：select * from sku_stock where sku_id in (value1,value2,.....)
         */
        skuStockLambdaQueryWrapper.in(SkuStock::getSkuId, skuIdList);
        List<SkuStock> skuStockList = skuStockMapper.selectList(skuStockLambdaQueryWrapper);
        //遍历skuStockList，获取skuId和totalNum
        //将skuStockList中的数据组装进map中，key是skuId，value是totalNum
        Map<Long, Integer> map = skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId, SkuStock::getTotalNum));
        productSkuList.forEach(productSku -> {
            //将skuStockList中的数据组装进productSku中
            //key是skuId，value是totalNum，所以可以通过productSku.getId()获取到key对应的value
            //然后就能把库存数量设置进productSku中
            productSku.setStockNum(map.get(productSku.getId()));
        });

        //查询商品详情
        LambdaQueryWrapper<ProductDetails> productDetailsLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productDetailsLambdaQueryWrapper.eq(ProductDetails::getProductId, id);
        //selectOne()，是查询一个，如果查询到多个，就会报错
        ProductDetails productDetails = productDetailsMapper.selectOne(productDetailsLambdaQueryWrapper);
        String[] split = productDetails.getImageUrls().split(",");
        //将ImageUrls中的多个图片地址组装进product中
        //asList()方法：只适用于不可变列表
        product.setDetailsImageUrlList(Arrays.asList(split));

        return product;

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateProduct(Product product) {
        //修改商品spu
        baseMapper.updateById(product);

        List<ProductSku> productSkuList = product.getProductSkuList();
        for (int i = 0; i < productSkuList.size(); i++) {
            //修改商品sku
            ProductSku productSku = productSkuList.get(i);
            productSkuMapper.updateById(productSku);

            //修改商品sku库存
            //修改库存信息
            LambdaQueryWrapper<SkuStock> skuStockLambdaQueryWrapper = new LambdaQueryWrapper<>();
            //通过库存id查询库存记录
            skuStockLambdaQueryWrapper.eq(SkuStock::getSkuId, productSku.getId());
            SkuStock skuStock = skuStockMapper.selectOne(skuStockLambdaQueryWrapper);
            //更新库存信息
            skuStock.setTotalNum(productSku.getStockNum());
            //总库存减去锁定库存就是可用总数
            Integer avaliable = productSku.getStockNum() - skuStock.getLockNum();
            skuStock.setAvailableNum(avaliable);
            skuStockMapper.updateById(skuStock);

        }
        //修改商品详细信息
        LambdaUpdateWrapper<ProductDetails> productDetailsLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        productDetailsLambdaUpdateWrapper.eq(ProductDetails::getProductId, product.getId());
        ProductDetails productDetails = new ProductDetails();
        productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
        productDetailsMapper.update(productDetails, productDetailsLambdaUpdateWrapper);

        return 1;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteProductByIds(List<Long> ids) {
        //删除商品spu
        baseMapper.deleteBatchIds(ids);

        //获取skuIdList，必须在这里获取，否则如果在下面获取，因为商品sku已经被删除，
        //所以会导致获取不了sku，进而导致空指针异常
        List<Long> skuIdList = productSkuMapper.selectList(
                new LambdaQueryWrapper<ProductSku>()
                        .in(ProductSku::getProductId, ids)
        ).stream().map(ProductSku::getId).toList();


        //删除商品sku
        //根据product_id去删除productSku中的所有的数据
        LambdaQueryWrapper<ProductSku> productSkuLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productSkuLambdaQueryWrapper.in(ProductSku::getProductId, ids);
        productSkuMapper.delete(productSkuLambdaQueryWrapper);

        //删除商品库存
        LambdaQueryWrapper<SkuStock> skuStockLambdaQueryWrapper = new LambdaQueryWrapper<>();
        skuStockLambdaQueryWrapper.in(SkuStock::getSkuId, skuIdList);
        skuStockMapper.delete(skuStockLambdaQueryWrapper);

        //删除商品详情图片
        productDetailsMapper.delete(
                new LambdaQueryWrapper<ProductDetails>()
                        .in(ProductDetails::getProductId, ids)
        );

        return 1;
    }

    @Override
    public void updateAuditStatus(Long id, Integer auditStatus) {
        Product product = new Product();
        product.setId(id);
        product.setAuditStatus(auditStatus);
        //1通过，-1不通过
        if (auditStatus == 1) {
            product.setAuditMessage("审核通过");
        } else if (auditStatus == -1) {
            product.setAuditMessage("审核不通过");
        } else {
            throw new ServiceException("非法参数");
        }
        baseMapper.updateById(product);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatus(Long id, Integer status) {
        Product product = new Product();
        product.setId(id);
        product.setStatus(status);
        if (status != 1 && status != -1) {
            throw new ServiceException("非法参数");
        }

        if (status == 1) {
            List<ProductSku> productSkuList = productSkuMapper.selectList(
                    new LambdaQueryWrapper<ProductSku>()
                            .eq(ProductSku::getProductId, id)
            );
            //将商品加入到布隆过滤器中
            RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("sku:bloom:filter");
            productSkuList.forEach(productSku -> {
                bloomFilter.add(productSku.getId());
            });

        }


        //更新sku的status
        LambdaUpdateWrapper<ProductSku> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ProductSku::getProductId, id)
                .set(ProductSku::getStatus, status);
        productSkuMapper.update(null, updateWrapper);

        baseMapper.updateById(product);
    }

    @Override
    public List<ProductSku> getTopSale() {
        return productSkuMapper.selectTopSale();
    }

    @Override
    public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
        return productSkuMapper.selectProductSkuList(skuQuery);
    }

    @Override
    public SkuPriceVo getSkuPrice(Long skuId) {
        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(ProductSku::getId, skuId)
                //我们只查询salePrice和marketPrice
                .select(ProductSku::getSalePrice, ProductSku::getMarketPrice);
        SkuPriceVo skuPriceVo = new SkuPriceVo();
        ProductSku productSku = productSkuMapper.selectOne(queryWrapper);
        skuPriceVo.setSalePrice(productSku.getSalePrice());
        skuPriceVo.setMarketPrice(productSku.getMarketPrice());
        skuPriceVo.setSkuId(skuId);

        //没有查询sku_id，不能使用这个函数
//        BeanUtils.copyProperties(productSku, skuPriceVo);

        return skuPriceVo;
    }

    /**
     * 获取商品详情页图片
     *
     * @param id
     * @return
     */
    @Override
    @GuiguCache(prefix = "product:productDetails:")
    public ProductDetails getProductDetails(Long id) {
        LambdaQueryWrapper<ProductDetails> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductDetails::getProductId, id);
        //只查询出一条记录
        return productDetailsMapper.selectOne(queryWrapper);
    }

    @GuiguCache(prefix = "product:skuSpecValue")
    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {

        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductSku::getId, id)
                .select(ProductSku::getSkuSpec, ProductSku::getId);

        List<ProductSku> productSkuList = productSkuMapper.selectList(queryWrapper);
        //将list结合转换成map集合
        Map<String, Long> map = productSkuList
                .stream()
                .collect(Collectors.toMap(ProductSku::getSkuSpec, ProductSku::getId));
        return map;
    }

    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        LambdaQueryWrapper<SkuStock> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SkuStock::getSkuId, skuId)
                .select(SkuStock::getAvailableNum, SkuStock::getSaleNum);

        SkuStockVo skuStockVo = new SkuStockVo();
        SkuStock skuStock = skuStockMapper.selectOne(queryWrapper);

        skuStockVo.setSkuId(skuId);
        skuStock.setAvailableNum(skuStock.getAvailableNum());
        skuStock.setSaleNum(skuStock.getSaleNum());
        return skuStockVo;

    }

    @GuiguCache(prefix = "product:product:")
    @Override
    public Product getProductById(Long id) {
        //查询数据库
        return baseMapper.selectById(id);
    }

    @Override
    public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        //根据skuIdList查询product_sku数据库
        queryWrapper.in(ProductSku::getId, skuIdList)
                //只查询商品的实时价格
                .select(ProductSku::getId, ProductSku::getSalePrice);
        //根据条件查询到商品列表
        List<ProductSku> productSkuList = productSkuMapper.selectList(queryWrapper);

        //将productSkuList中的数据组装到List<SkuPriceVo>中
        List<SkuPriceVo> skuPriceVoList = productSkuList.stream()
                .map(productSku -> {
                    SkuPriceVo skuPriceVo = new SkuPriceVo();
//                    BeanUtils.copyProperties(productSku, skuPriceVo);
                    skuPriceVo.setSkuId(productSku.getId());
                    skuPriceVo.setSalePrice(productSku.getSalePrice());
                    return skuPriceVo;
                }).collect(Collectors.toList());
        return skuPriceVoList;
    }

    /**
     * 检查锁定库存
     *
     * @param orderNo       订单编号
     * @param skuLockVoList 锁定的库存列表
     * @return xx商品库存不足的错误提示消息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        //锁定库存的业务不能够被重复调用，因此我们需要防止重复请求
        String key = "sku:checkAndLock:" + orderNo;

        //防止重复请求
        //如果是第一次执行，返回ture，不是的话，返回提示信息
        Boolean isFirst = redisTemplate.opsForValue()
                .setIfAbsent(
                        key,
                        orderNo,
                        1,
                        TimeUnit.HOURS);
        //如果不是第一次执行
        if (!isFirst) {
            return "请不要重复提交";
        }

        //校验每一个sku的库存是否充足
        skuLockVoList.forEach(skuLockVo -> {
            //检查数据库中是否有库存
            SkuStock skuStock = skuStockMapper.check(
                    skuLockVo.getSkuId(),
                    skuLockVo.getSkuNum());
            //如果能查询到记录，证明有库存，否则就没有库存
            if (skuStock == null) {
                //库存不足
                skuLockVo.setHaveStock(false);
            } else {
                //库存充足
                skuLockVo.setHaveStock(true);
            }
        });

        /*
        判断库存列表中是否有库存不足的商品
        anyMatch()方法，只有有任何一个条件满足，返回true，否则返回false
        用户在购物车中购买商品有可能会买多个，所以通常情况下，
        只要这个购物车列表中有一个商品的库存不足，那么我们就认定条件成立
         */
        boolean checkResult = skuLockVoList.stream()
                .anyMatch(skuLockVo -> !skuLockVo.isHaveStock());
        if (checkResult) {
            //条件成立，过滤库存不足的商品
            List<SkuLockVo> insufficientInventoryList = skuLockVoList.stream()
                    .filter(skuLockVo -> !skuLockVo.isHaveStock())
                    .collect(Collectors.toList());

            StringBuffer result = new StringBuffer();
            for (SkuLockVo skuLockVo : insufficientInventoryList) {
                result.append("商品：")
                        .append(skuLockVo.getSkuId())
                        .append("库存不足")
                        .append("\n");
            }

            //如果锁定库存失败，就可以把key直接删除掉
            redisTemplate.delete(key);

            return String.valueOf(result);

        } else {
            //所有商品库存都充足则锁定库存
            for (SkuLockVo skuLockVo : skuLockVoList) {
                //锁定库存
                skuStockMapper.lock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            }
        }

        //商品成功锁定库存时，将skuLockVoList存入redis，
        //方便后期扣减库存（支付成功），或者解锁库存（取消订单）
        String dataKey = "sku:lock:data:" + orderNo;
        redisTemplate.opsForValue().set(dataKey, skuLockVoList);

        return "";
    }

    @Override
    public void minus(String orderNo) {
        //防止重复请求
        //如果是第一次执行，返回ture，不是的话，返回提示信息
        String key = "sku:minus:" + orderNo;
        Boolean isFirst = redisTemplate.opsForValue()
                .setIfAbsent(
                        key,
                        orderNo,
                        1,
                        TimeUnit.HOURS);
        //如果不是第一次执行
        if (!isFirst) {
            return;
        }


        String dataKey = "sku:lock:data:" + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate
                .opsForValue()
                .get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)) {
            return;
        }

        //扣减库存
        skuLockVoList.forEach(skuLockVo -> {
            skuStockMapper.minus(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
        });

        //扣减库存成功之后，删除redis中的数据
        redisTemplate.delete(dataKey);

    }

    @Override
    public void unlock(String orderNo) {
        //防止重复请求
        //如果是第一次执行，返回ture，不是的话，返回提示信息
        String key = "sku:unlock:" + orderNo;
        Boolean isFirst = redisTemplate.opsForValue()
                .setIfAbsent(
                        key,
                        orderNo,
                        1,
                        TimeUnit.HOURS);
        //如果不是第一次执行
        if (!isFirst) {
            return;
        }


        String dataKey = "sku:lock:data:" + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate
                .opsForValue()
                .get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)) {
            return;
        }

        //解锁库存
        skuLockVoList.forEach(skuLockVo -> {
            skuStockMapper.unlock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
        });

        //解锁库存成功之后，删除redis中的数据
        redisTemplate.delete(dataKey);

    }

}
